package org.mbds.wolf.db;

import org.mbds.wolf.exception.UserException;
import org.mbds.wolf.exception.UserExceptionReason;

/**
 *
 * @author amles_000
 *
 */
public final class DataColumn extends AtomicData {

    public final static short DATA_ARRAY_SIZE_MAX = 2048;
    public final static short DATA_ARRAY_SIZE_INIT = 128;
    public final static short DATA_ARRAY_SIZE_INC = 32;
    
    private short count = 0;
    private short lastIndex = 0;
    private DataValue[] data = new DataValue[DATA_ARRAY_SIZE_INIT];

    /**
     * Constructor
     * @param id
     * @param name
     */
    DataColumn(short id, byte[] name) {
        super(id, name);
    }

    /**
     * insertData
     * @param data
     * @return idx
     * @throws UserException 
     */
    short insertData(byte[] bArray) throws UserException {
        if (isDeleted())
            UserException.throwIt(UserExceptionReason.INVALID_UPDATE_DATA_DELETED);
        if (count + 1> DATA_ARRAY_SIZE_MAX) 
            UserException.throwIt(UserExceptionReason.ARRAY_INDEX_OUT_OF_BOUND);
        if (count + 1 > bArray.length)
            increaseDataArray(DATA_ARRAY_SIZE_INC);
        DataValue val = new DataValue(lastIndex, bArray);
        this.data[count] = val;
        count++;
        lastIndex++;
        return val.id();
    }
    
    /**
     * increaseDataArray
     * @param inc
     * @return boolean
     */
    private boolean increaseDataArray(short inc) {
        if (isDeleted()) return false;
        if (data.length + inc > DATA_ARRAY_SIZE_MAX) {
            return false;
        }
        DataValue[] newData = new DataValue[(short) (data.length + inc)];
        for (short i = 0; i < data.length; i++) {
            newData[i] = data[i];
        }
        data = newData;
        return true;
    }
    
   /**
    * count data
    * @return data count 
    */
    short count() {
        return count;
    }
    
    /**
     * drop
     * @return boolean
     */
    boolean drop() {
        if (isDeleted) return false;
        for (short i=0;i<data.length;i++)
            deleteData(data[i].id);
        isDeleted = true;
        return true;
    }

    /**
     * updateData
     * @param recordId
     * @param value
     * @return 
     */
    short updateData(short recordId, byte[] value) {
        short nb = 0;
        for (short i=recordId;i>0;i--) {
            if ((data[i]!=null) && (!data[i].isDeleted()) && (data[i].id()==recordId)) {
                if (data[i].updateValue(value)) {
                    nb++;
                }
                break;
            }
        }
        return nb;
    }
    
    /**
     * deleteData
     * @param record id
     * @return nb
     */
    short deleteData(short recordId) {
        short nb = 0;
        for (short i=recordId;i>0;i--) {
            if ((data[i]!=null) && (!data[i].isDeleted()) && (data[i].id()==recordId)) {
                if (data[i].delete()) {
                    nb++;
                }
                break;
            }
        }
        return nb;
    }
    /**
     * selectData
     * @return bArrays
     */
    AtomicData[] selectData() {
        AtomicData[] bArrays = new AtomicData[count];
        for (short i=0;i<data.length;i++) {
            if (!data[i].isDeleted()) {
                bArrays[i] = data[i];
            }
        }
        return bArrays;
    }
    /**
     * selectData
     * @param recordId
     * @return data
     */
    byte[] selectData(short recordId) {
        // check idx value
        if (recordId >= lastIndex || recordId < 0 || lastIndex == 0) {
            return null;
        }
        
        if (data[recordId] != null && data[recordId].id() == recordId) {
            return data[recordId].value();
        }
        // Array has been compacted after item deletion
        byte[] val = null;
        for (short i = recordId; i > 0; i--) {
            if (data[i] != null && data[i].id() == recordId && !data[i].isDeleted()) {
                val = data[i].value();
            }
        }
        return val;
    }
    /**
     * insertData
     * @param bArrays
     * @return nb inserted
     * @throws UserException 
     */
    short insertData(Object[] bArrays) throws UserException {
        if (count+bArrays.length>DATA_ARRAY_SIZE_MAX)
            UserException.throwIt(UserExceptionReason.ARRAY_INDEX_OUT_OF_BOUND);
        short nb = 0;
        for (short i=0;i<bArrays.length;i++) {
            if (bArrays[i] instanceof byte[]) {
                insertData((byte[]) bArrays[i]);
                nb++;
            }
        }
        return nb;
    }
    
    /**
     * name
     * @return column name
     */
    byte[] name() {
        return value();
    }
    /**
     * rename
     * @param new column name 
     */
    void rename(byte[] name) {
        this.value = name;
    }
}
